package com.hzhq.contract.util;

import org.springframework.util.StopWatch;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author hzhq
 * @create 2021/4/9 2:45 下午
 * @email hzhq1255@163.com
 */
public class MyBeanUtils {


    private static List<String> targetPropertyNames;


    private static PropertyDescriptor[] getPropertyDescriptors(Object object) throws IntrospectionException {
        BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
        return beanInfo.getPropertyDescriptors();
    }

    private static List<String> getPropertyNames(Object object) throws IntrospectionException {
        BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        return Arrays.stream(propertyDescriptors).map(PropertyDescriptor::getName).collect(Collectors.toList());
    }

    /**
     * 动态复制
     *
     * @param source 源对象
     * @param target 目标对象
     * @return 目标对象
     */
    public static void dynamicCopySameBean(Object source, Object target) {
        if (!source.getClass().isInstance(target)) {
            return;
        }
        try {
            for (Field field : source.getClass().getDeclaredFields()) {
                field.setAccessible(true);
//                if (null == field.get(source)){
//                    continue;
//                }
                String key = field.getName();
                System.out.println(key);
                String getMethodName = "get" + key.substring(0, 1).toUpperCase() + key.substring(1);
                String setMethodName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1);
                Method getMethod = source.getClass().getDeclaredMethod(getMethodName);
                Object value = getMethod.invoke(source);
//                target.getClass().getMethod(setMethodName,value.getClass()).invoke(target,value);
            }
        } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }


    /**
     * 带忽略null字段的对象字段复制
     *
     * @param source          源对象
     * @param target          目标对象
     * @param ignoreNullValue 是否忽略 null字段
     * @param <T>             泛型
     */
    public static <T> void copyProperties(T source, T target, boolean ignoreNullValue) {
        try {
            List<String> targetPropertyNames = new ArrayList<>();
            if (!source.getClass().isInstance(target)) {
                targetPropertyNames = getPropertyNames(target);
            }
            for (PropertyDescriptor propertyDescriptor : getPropertyDescriptors(source)) {
                String key = propertyDescriptor.getName();
                Object value = propertyDescriptor.getReadMethod().invoke(source);
                boolean isKeyContain = targetPropertyNames.size() == 0 || targetPropertyNames.contains(key);
                if (!"class".equals(key) && isKeyContain ) {
                    Method setMethod = new PropertyDescriptor(key, target.getClass()).getWriteMethod();
                    if (!ignoreNullValue) {
                        setMethod.invoke(target, value);
                    } else {
                        if (null != value) {
                            setMethod.invoke(target, value);
                        }
                    }
                }
            }
        } catch (IntrospectionException | InvocationTargetException | IllegalAccessException | NullPointerException e) {
            e.printStackTrace();
        }
    }


    /**
     * 复制对象属性 能指定忽略属性
     *
     * @param source           源对象
     * @param target           目标对象
     * @param ignoreValueNames 忽略的属性
     * @param <T>              泛型
     */
    public static <T> void copyProperties(T source, T target, String... ignoreValueNames) {
        try {
            List<String> targetPropertyNames = new ArrayList<>();
            if (!source.getClass().isInstance(target)) {
                targetPropertyNames = getPropertyNames(target);
            }
            for (PropertyDescriptor propertyDescriptor : getPropertyDescriptors(source)) {
                String key = propertyDescriptor.getName();
                Object value = propertyDescriptor.getValue(key);
                boolean isKeyContain = targetPropertyNames.size() == 0 || targetPropertyNames.contains(key);
                if (!"class".equals(key) && isKeyContain && Arrays.stream(ignoreValueNames).noneMatch(Predicate.isEqual(key))) {
                    Method setMethod = new PropertyDescriptor(key, target.getClass()).getWriteMethod();
                    if (setMethod != null) {
                        setMethod.invoke(target, value);
                    }
                }
            }
        } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 默认复制字段
     *
     * @param source 源对象
     * @param target 目标对象
     * @param <T>    泛型
     */
    public static <T> void copyProperties(T source, T target) {
        try {
            List<String> targetPropertyNames = new ArrayList<>();
            if (!source.getClass().isInstance(target)) {
                targetPropertyNames = getPropertyNames(target);
            }
            for (PropertyDescriptor propertyDescriptor : getPropertyDescriptors(source)) {
                String key = propertyDescriptor.getName();
                Object value = propertyDescriptor.getValue(key);
                boolean isKeyContain = targetPropertyNames.size() == 0 || targetPropertyNames.contains(key);
                if (!"class".equals(key) && isKeyContain ) {
                    Method setMethod = new PropertyDescriptor(key, target.getClass()).getWriteMethod();
                    setMethod.invoke(target, value);

                }
            }
        } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static void test() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
    }


    public static void main(String[] args) {
        Person person1 = new Person(null, "zhangsan", 20);
        Person person2 = new Person(1, "lisi", 19);
        Student student = new Student("wangwu", 80);
        copyProperties(person1, student, true);
        copyProperties(person1, person2,true);
        System.out.println(person1 + "\n" + person2 + "\n" + student);
    }


}
